import fetch from 'node-fetch';
import fs from 'fs';
import path from 'path';
import qrcode from 'qrcode';
import { createHash } from 'crypto';

// app签名
function appSign(params, appkey, appsec) {
    const paramsWithAppkey = { ...params, appkey };
    const sortedParams = Object.fromEntries(Object.entries(paramsWithAppkey).sort());
    const query = new URLSearchParams(sortedParams).toString();
    const sign = md5(query + appsec);
    return { ...sortedParams, sign };
}

const md5 = (str) => createHash('md5').update(str).digest('hex');

const storagePath = './temp/bilibili';
const appkey = '27eb53fc9058f8c3'; // appkey
const appsec = 'c2ed53a74eeefe3cf99fbd01d8c9c375'; // appsecret

export class ExamplePlugin extends plugin {
    constructor() {
        super({
            name: "b站功能",
            desc: "扫码登录",
            event: "message",
            priority: -Infinity,
            rule: [
                {
                    reg: /^(b站|哔站|逼站|币站)(扫码)?(登陆|登录)$/,
                    fnc: "bililogin"
                },{
                    reg: /^刷新(b站|哔站|逼站|币站)ck$/,
                    fnc: "refreshUserToken"
                }
            ]
        });
        this.storagePath = storagePath;
    }

    async  refreshUserToken(e) {
        const cookieFile = path.join(storagePath, 'user_cookies.json');
        let cookies = {};
        try {
            cookies = JSON.parse(fs.readFileSync(cookieFile, 'utf8'));
        } catch (err) {
            logger.error("无法读取cookie文件:", err);
            e.reply("未能绑定cookie，请发送b站扫码登录进行绑定", true);
            return;
        }
    
        const userCookies = cookies[e.user_id];
        if (!userCookies || !userCookies.access_token || !userCookies.refresh_token) {
            e.reply("未能找到有效的token信息，请发送b站扫码登录进行绑定", true);
            return;
        }
            const sessdata = userCookies.SESSDATA;
            const csrf = userCookies.csrf;
            const DedeUserID = userCookies.DedeUserID;
            const DedeUserID__ckMd5 = userCookies.DedeUserID__ckMd5;
            const sid = userCookies.sid;
            const access_token = userCookies.access_token;
            
        const ts = Math.floor(Date.now() / 1000);
        const signedParams = appSign({
            access_key: userCookies.access_token,
            refresh_token: userCookies.refresh_token,
            ts: ts
        }, appkey, appsec);
    
        const url = 'https://passport.bilibili.com/api/v2/oauth2/refresh_token';
        const headers = {
            'Content-Type': 'application/x-www-form-urlencoded',
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36',
            'Cookie': `access_token=${access_token}; bili_jct=${csrf}; DedeUserID=${DedeUserID}; DedeUserID__ckMd5=${DedeUserID__ckMd5}; sid=${sid}; SESSDATA=${sessdata}`
        };
    
        try {
            const response = await fetch(url, {
                method: 'POST',
                body: new URLSearchParams(signedParams),
                headers: headers
            });
    
            if (response.ok) {
                const data = await response.json();
                if (data.code === 0) {
                    // 更新用户 cookies 文件
                    const updatedCookies = {
                        access_token: data.data.token_info.access_token,
                        SESSDATA: data.data.cookie_info.cookies.find(c => c.name === 'SESSDATA')?.value,
                        csrf: data.data.cookie_info.cookies.find(c => c.name === 'bili_jct')?.value,
                        DedeUserID: data.data.cookie_info.cookies.find(c => c.name === 'DedeUserID')?.value,
                        DedeUserID__ckMd5: data.data.cookie_info.cookies.find(c => c.name === 'DedeUserID__ckMd5')?.value,
                        sid: data.data.cookie_info.cookies.find(c => c.name === 'sid')?.value,
                        refresh_token: data.data.token_info.refresh_token,
                    };
    
                    const userFile = path.join(storagePath, 'user_cookies.json');
                    let users = {};
                    if (fs.existsSync(userFile)) {
                        users = JSON.parse(fs.readFileSync(userFile, 'utf-8'));
                    }
    
                    users[e.user_id] = updatedCookies;
                    fs.writeFileSync(userFile, JSON.stringify(users, null, 4));
                    
                    // 计算并回复有效期
                    const expires_in = data.data.token_info.expires_in;
                    const expirationTime = new Date(ts * 1000 + expires_in * 1000).toLocaleString();
                    e.reply(`token刷新成功，有效期至：${expirationTime}`, true);
                } else {
                    e.reply(`token刷新失败: ${data.message}`, true);
                }
            } else {
                e.reply(`请求失败，状态码: ${response.status}`, true);
            }
        } catch (error) {
            logger.error("请求错误详情:", error);
            e.reply('发生错误，请稍后再试', true);
        }
    }

    async bililogin(e) {
        if(await redis.get(`login:${e.user_id}`)) return e.reply("前置二维码未失效，请稍后尝试", true)
        if (!fs.existsSync(this.storagePath)) {
            fs.mkdirSync(this.storagePath, { recursive: true });
        }

        try {
            const qrInfo = await this.getQrCode();
             logger.mark("二维码信息:", qrInfo);
             redis.set(`${e.user_id}`, `${e.group_id}`)
            const saveCodePath = path.join(this.storagePath, `qrcode.png`);
            await qrcode.toFile(saveCodePath, qrInfo.data.url);
            await this.e.reply('免责声明:\n您将通过扫码完成获取哔哩哔哩的ck用于请求B站API接口以获取数据。\n本Bot不会保存您的登录状态。\n我方仅提供相关B站内容服务,若您的账号封禁、被盗等处罚与我方无关。\n害怕风险请勿扫码 ~', true, { recallMsg: 60 })
            redis.set(`login:${e.user_id}`, "1", { EX: 120 })
            e.reply([segment.image(saveCodePath), '请在90秒内使用哔站进行扫码'], true, { recallMsg: 90 });

            let pollCount = 0;
            const maxPolls = 18; // 最大尝试次数
            const intervalTime = 5000; // 每次轮询间隔时间

            const pollRequest = async () => {
                try {
                    const result = await this.checkQrCodeStatus(qrInfo.data.auth_code);
                     logger.mark("轮询结果:", result);

                    if (result.code === 0 && result.data) {
                        const data = result.data;
                        const sessDataMatch = data.cookie_info.cookies.find(cookie => cookie.name === 'SESSDATA');
                        const biliJctMatch = data.cookie_info.cookies.find(cookie => cookie.name === 'bili_jct');

                        if (sessDataMatch && biliJctMatch) {
                            const cookies = data.cookie_info.cookies.reduce((acc, cookie) => {
                                acc[cookie.name] = cookie.value;
                                return acc;
                            }, {});

                            const userFile = path.join(this.storagePath, 'user_cookies.json');
                            let users = {};

                            if (fs.existsSync(userFile)) {
                                users = JSON.parse(fs.readFileSync(userFile, 'utf-8'));
                            }

                            users[e.user_id] = {
                                access_token: data.access_token,
                                SESSDATA: cookies.SESSDATA,
                                csrf: cookies.bili_jct,
                                DedeUserID: cookies.DedeUserID,
                                DedeUserID__ckMd5: cookies.DedeUserID__ckMd5,
                                sid: cookies.sid,
                                refresh_token: data.refresh_token,
                            };

                            fs.writeFileSync(userFile, JSON.stringify(users, null, 4));
                            logger.mark(`用户数据已保存到 ${userFile}`);
                            e.reply('登录成功！', true);
                            redis.del(`login:${e.user_id}`)
                        } else {
                            logger.warn("无法从URL中提取SESSDATA或bili_jct");
                            e.reply('无法获取登录信息，请稍后再试', true);
                            redis.del(`login:${e.user_id}`)
                        }
                    } else if (result.code === 86038) {
                        logger.warn("二维码已失效");
                        e.reply('二维码已失效，请重新尝试', true);
                        redis.del(`login:${e.user_id}`)
                    } else if (result.code === 86090) {
                        logger.warn("二维码已扫码但未确认");
                        let key = `bili:${e.user_id}`;
                        if (!(await redis.get(key))) {
                            redis.set(key, "1", { EX: 50 });
                            e.reply('扫码成功，请确认登录', true);
                        }
                        if (pollCount < maxPolls) {
                            pollCount++;
                            setTimeout(pollRequest, intervalTime);
                        } else {
                            logger.warn("二维码超时未确认");
                            e.reply('二维码超时未确认，请重新尝试', true);
                            redis.del(`login:${e.user_id}`)
                        }
                    } else if (pollCount < maxPolls) {
                        pollCount++;
                        setTimeout(pollRequest, intervalTime);
                    } else {
                        logger.warn("二维码超时未确认");
                        e.reply('二维码超时未确认，请重新尝试', true);
                        redis.del(`login:${e.user_id}`)
                    }
                } catch (error) {
                    logger.error(error);
                    e.reply('发生错误，请稍后再试', true);
                    redis.del(`login:${e.user_id}`)
                }
            };

            pollRequest();
        } catch (error) {
            logger.error(error);
            e.reply('获取二维码失败，请稍后再试', true);
            redis.del(`login:${e.user_id}`)
        }
    }

    async getQrCode() {
        const ts = Math.floor(Date.now() / 1000);
        const params = {
            local_id: 0,
            ts: ts
        };

        const signedParams = appSign(params, appkey, appsec);

        const url = 'https://passport.snm0516.aisee.tv/x/passport-tv-login/qrcode/auth_code';
        const headers = {
            'referer': 'https://www.bilibili.com/',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'
        };

        try {
            const response = await fetch(url, {
                method: 'POST',
                body: new URLSearchParams(signedParams),
                headers: headers
            });

            if (response.ok) {
                const data = await response.json();
                 logger.info("响应数据:", data);
                if (data.code === 0) {
                    return data;
                } else {
                    throw new Error(`获取二维码失败: ${data.message}`);
                }
            } else {
                throw new Error(`请求失败，状态码: ${response.status}`);
            }
        } catch (error) {
             logger.error("请求错误详情:", error);
            throw error;
        }
    }

    async checkQrCodeStatus(auth_code) {
        const ts = Math.floor(Date.now() / 1000);
        const params = {
            auth_code: auth_code,
            local_id: 0,
            ts: ts
        };

        const signedParams = appSign(params, appkey, appsec);

        const url = 'https://passport.snm0516.aisee.tv/x/passport-tv-login/qrcode/poll';
        const headers = {
            'referer': 'https://www.bilibili.com/',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'
        };

        try {
            const response = await fetch(url, {
                method: 'POST',
                body: new URLSearchParams(signedParams),
                headers: headers
            });

            if (response.ok) {
                return await response.json();
            } else {
                throw new Error(`请求失败，状态码: ${response.status}`);
            }
        } catch (error) {
           logger.error("请求错误详情:", error);
            throw error;
        }
    }
}